package cs348a.data;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * <code>EdgeFileData</code> contains the site edge data. It parses a ".edges" file
 * and creates an internal data structure.
 * 
 * @author H.S. Kim
 *
 */
public class EdgeFileData {
	static public String EDGE = "edge";
	static public String SITE = "site";
	
	/** List of sites */
	public ArrayList<Site> siteList = new ArrayList<Site>();
	
	/** List of edges */
	public ArrayList<Edge> edgeList = new ArrayList<Edge>();

	/** List of edges */
	public ArrayList<Triangle> triangleList = new ArrayList<Triangle>();
	
	// Stats of site data
	public int minX = Integer.MAX_VALUE;
	public int maxX = Integer.MIN_VALUE;

	public int minY = Integer.MAX_VALUE;
	public int maxY = Integer.MIN_VALUE;

	public int minZ = Integer.MAX_VALUE;
	public int maxZ = Integer.MIN_VALUE;
	
	
	/**
	 * Constructor
	 */
	public EdgeFileData() {
	}
	
	/**
	 * Returns the number of sites.
	 * 
	 * @return
	 */
	public int numSites() {
		return siteList.size();
	}
	
	/**
	 * Returns the number of edges. 
	 * 
	 * @return
	 */
	public int numEdges() {
		return edgeList.size();
	}
	
	/**
	 * Returns the number of triangles. 
	 * 
	 * @return
	 */
	public int numTriangles() {
		return triangleList.size();
	}
	
	/**
	 * Parses the data file to create data structures
	 * 
	 * @param file
	 */
	public void parseDataFile(File file) {		
		
		try {
			BufferedReader input =  new BufferedReader(new FileReader(file));
			
			try {
				//TODO: clear after parsing is successful
				resetData();
				
				String line = null;
				
				while (( line = input.readLine()) != null){
					Scanner scanner = new Scanner(line);
					scanner.useDelimiter(" ");
					
					if(scanner.hasNext()) {
						String type = scanner.next();
						if(type.compareTo(SITE) == 0) { // handle edge
							int x = new Integer(scanner.next());
							int y = new Integer(scanner.next());
							int z = new Integer(scanner.next());
							int i = new Integer(scanner.next());

							if(i != siteList.size()){ 
								// the site index must match the array index
								// we will NOT search for the site
								throw new Exception("Index does not match - site index/array index: "+i+"/"+siteList.size());
							} else {
								siteList.add(new Site(x, y, z, i));
								
								// get stats
								if(x < minX) minX = x;
								if(x > maxX) maxX = x;
								if(y < minY) minY = y;
								if(y > maxY) maxY = y;
								if(z < minZ) minZ = z;
								if(z > maxZ) maxZ = z;
							}
							
						} else if(type.compareTo(EDGE) == 0) { // handle site
							int org = new Integer(scanner.next());
							int dst = new Integer(scanner.next());
							
							edgeList.add(new Edge(org, dst));
						}
					}
				}
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			finally {
				input.close();
			}
			
			
		}
	    catch (IOException e){
	      e.printStackTrace();
	    }
	}
	
	/**
	 * Creates triangles from the data
	 */
	public void triangulate() {
		
		ArrayList<ArrayList<Integer>> neighbors = new ArrayList<ArrayList<Integer>>(siteList.size());
		for(int i = 0; i < siteList.size(); i++) { // initialize
			neighbors.add(new ArrayList<Integer>());
		}
		
		// for each edge
		for(Edge e : edgeList) {
			// find triangle
			findTriangles(e, neighbors);
			
			// update neighbors
			neighbors.get(e.orgIndex).add(e.dstIndex);
			neighbors.get(e.dstIndex).add(e.orgIndex);
		}
	}

	/**
	 * Finds a new triangle
	 * 
	 * @param e
	 * @param n
	 * @return
	 */
	private void findTriangles(Edge e, ArrayList<ArrayList<Integer>> n) {
		// find intersection of neighbors
		ArrayList<Integer> nOrg = n.get(e.orgIndex);
		ArrayList<Integer> nDst = n.get(e.dstIndex);
		
		List<Integer> s = findIntersections(nOrg, nDst);
		if(s.size() > 0) {
			for(Integer i : s) {
//				 TODO: handle normals while creating file
				triangleList.add(new Triangle(i, e.dstIndex, e.orgIndex));
			}
		}
	}

	/**
	 * Finds the intersection
	 * 
	 * @param org
	 * @param dst
	 * @return
	 */
	private List<Integer> findIntersections(ArrayList<Integer> org, ArrayList<Integer> dst) {
		ArrayList<Integer> crossList = new ArrayList<Integer>();
		
		for(Integer i : org) {
			for(Integer j : dst) {
				if(i.compareTo(j) == 0) {
					crossList.add(i);
				}
			}
		}
		
		return crossList;
	}

	/**
	 * Resets the data
	 */
	private void resetData() {
		siteList.clear();
		edgeList.clear();
		triangleList.clear();
		
		minX = Integer.MAX_VALUE;
		maxX = Integer.MIN_VALUE;

		minY = Integer.MAX_VALUE;
		maxY = Integer.MIN_VALUE;

		minZ = Integer.MAX_VALUE;
		maxZ = Integer.MIN_VALUE;
	}
}
